﻿/*************************************************************************************

- CLR版本：        4.0.30319.42000
- 类 名 称：       DMC5400A
- 机器名称：       YTE
- 命名空间：       LD.BC.SMC
- 文 件 名：       DMC5400A
- 创建时间：       2022/6/11 15:11:46
- 作    者：       YTE
- 说   明：
- 修改时间：
- 修 改 人：
  *************************************************************************************/

using LD.Host;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace LD.BC
{
    [Bc(Name = "雷赛DMC5400A", Brand = BrandType.DMC5400A)]
    class DMC5400A : LTDMC, IBoardCard
    {
        private string[] sportModes = { "定长运动", "定速运动", "回零运动", "往复运动" };
        private string[] stopModes = { "减速停止", "立即停止" };
        private string[] locationModes = { "相对坐标", "绝对坐标" };
        private string[] directionModes = { "负方向", "正方向" };
        private string[] zeroModes = { "一次回零", "一次回零+反找", "两次回零", "一次回零+EZ", "EZ回零", "一次回零+反找"
        ,"原点锁存","原点+EZ锁存","EZ锁存","原点+反向EZ锁","限位一次回零","限位回零反找","限位两次回零" };
        private string[] speedModes = { "低速回零", "高速回零" };
        private string[] pluseModes = { "脉冲/方向", "脉冲/脉冲" };

        public Dictionary<string, string[]> GetParam()
        {
            Dictionary<string, string[]> param = new Dictionary<string, string[]>
            {
                { "sportModes", sportModes },
                { "stopModes", stopModes },
                { "locationModes", locationModes },
                { "directionModes", directionModes },
                { "zeroModes", zeroModes },
                { "speedModes", speedModes },
                { "pluseModes", pluseModes }
            };
            return param;
        }

        private TaskEx TaskEx = new TaskEx();
        private ushort _bcId;
        #region 监控
        public void Start()
        {
            Log4.Info("板卡监控启动");
            TaskEx.Start(Monitor, null);
        }

        #endregion

        #region 连接
        public void Connection()
        {
            short num = LTDMC.dmc_board_init();//获取卡数量
            if (num <= 0 || num > 8)
            {
                Log4.Error("板卡初始化失败,未检测到板卡,请检查驱动");
                throw new Exception("板卡初始化失败");
            }
            ushort _num = 0;
            ushort[] cardids = new ushort[8];
            uint[] cardtypes = new uint[8];
            short res = LTDMC.dmc_get_CardInfList(ref _num, cardtypes, cardids);
            if (res != 0)
            {
                Log4.Error("板卡初始化失败");
                throw new Exception("板卡初始化失败");
            }
            _bcId = cardids[0];
            uint count = 0;
            LTDMC.dmc_get_total_axes(cardids[0], ref count);
        }
        public void Closed()
        {
            TaskEx?.Cancel();
            uint posValue = 0xFFFF;
            dmc_write_outport(_bcId, 0, posValue);
            dmc_board_close();
        }

        #endregion

        #region 轴运动
        public void Stop(ushort axis)
        {
            dmc_stop(_bcId, axis, 1);
        }
        public void StopALL()
        {
            dmc_emg_stop(_bcId);
        }
        /// <summary>
        /// 等轴运行结束
        /// </summary>
        /// <param name="cardNo"></param>
        /// <param name="axis"></param>
        public void WaitAxisComplete(ushort axis)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            while (dmc_check_done(_bcId, axis) == 0 && stopwatch.ElapsedMilliseconds < (30 * 1000))// 判断轴运动状态
            {
                Task.Delay(50).Wait();
            }
            stopwatch.Stop();
        }
        public void WaitAxisComplete()
        {
            //foreach (var item in BcFactory.AxisParamList)
            //{
            //    int i = item.AxisId;
            //    WaitAxisComplete((ushort)i);
            //}
        }

        /// <summary>
        /// 相对移动
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="ration_v"></param>
        public void Move_Ration(ushort axis, double ration_v)
        {
            if (BCAlertHelper.GetErrorQty) return;
            var moveP = BcFactory.Instance.Config.AxisParam.Where(p => p.AxisId == axis).FirstOrDefault();
            if (moveP == null)
            {
                throw new Exception("轴参数无设置");
            }
            ushort MyCardNo, Myaxis, Myposi_mode;
            double MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel;
            double MyDist;
            MyCardNo = _bcId;                                               //卡号
            Myaxis = axis;                                                  //轴号
            MyMin_Vel = (double)moveP.Min_val;                              //起始速度 200pulse/s
            MyMax_Vel = (double)moveP.Max_val;                              //最大速度 5000pulse/s
            MyTacc = (double)moveP.Tacc;                                    //加速时间 0.01s
            MyTdec = (double)moveP.Tdec;                                    //减速时间 0.01s
            MyStop_Vel = (double)moveP.Stop_val;                            //停止速度 200pulse/s 
            MyDist = ration_v;                                              //位移为 6000pulse
            Myposi_mode = 0;                                                //0:相对，1:绝对坐标

            if (BcFactory.Instance.RunModel == StateEnum.手动模式)
                MyMax_Vel = (double)moveP.Manual_val;

            LTDMC.dmc_set_profile_unit(MyCardNo, Myaxis, MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel);
            LTDMC.dmc_set_s_profile(MyCardNo, Myaxis, 0, (double)moveP.S_Para);//设置单轴运动速度曲线 
            LTDMC.dmc_pmove_unit(MyCardNo, Myaxis, MyDist, Myposi_mode);    //执行点位运动
        }
        /// <summary>
        /// 轴移动(寸动)，
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="direction">方向：0/反方向，1/正方向</param>
        public void Move(ushort axis, ushort direction)
        {
            if (BCAlertHelper.GetErrorQty) return;
            var moveP = BcFactory.Instance.Config.AxisParam.Where(p => p.AxisId == axis).FirstOrDefault();
            if (moveP == null)
            {
                throw new Exception("轴参数无设置");
            }
            ushort MyCardNo, Myaxis;
            double MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel;
            MyCardNo = _bcId;                               //卡号
            Myaxis = axis;                                                  //轴号
            MyMin_Vel = (double)moveP.Min_val;                              //起始速度 200pulse/s
            MyMax_Vel = (double)moveP.Max_val;                              //最大速度 5000pulse/s
            MyTacc = (double)moveP.Tacc;                                    //加速时间 0.01s
            MyTdec = (double)moveP.Tdec;                                    //减速时间 0.01s
            MyStop_Vel = (double)moveP.Stop_val;                            //停止速度 200pulse/s 

            if (BcFactory.Instance.RunModel == StateEnum.手动模式)
                MyMax_Vel = (double)moveP.Manual_val;

            LTDMC.dmc_set_profile_unit(MyCardNo, Myaxis, MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel);
            LTDMC.dmc_set_s_profile(MyCardNo, Myaxis, 0, (double)moveP.S_Para);//设置单轴运动速度曲线 
            LTDMC.dmc_vmove(MyCardNo, Myaxis, direction);                   //执行运动
        }
        /// <summary>
        /// 回零
        /// </summary>
        /// <param name="axis"></param>
        public void Move_BackZero(ushort axis)
        {
            if (BCAlertHelper.GetErrorQty) return;
            var moveP = BcFactory.Instance.Config.AxisParam.Where(p => p.AxisId == axis).FirstOrDefault();
            if (moveP == null)
            {
                throw new Exception("轴参数无设置");
            }
            ushort MyCardNo, Myaxis, Myorg_logic, Myfilter, Myhome_dir, Mymode, MyEZ_count;
            double Myvel_mode;
            MyCardNo = _bcId;                                       //卡号
            Myaxis = axis;                                          //轴号
            Myorg_logic = 0;                                        //原点信号有效电平为低电平有效
            Myfilter = 0;                                           //保留参数
            Myhome_dir = (ushort)moveP.Zero_dir;                    //回零方向为正向
            Myvel_mode = 1;                                         //回零高低速
            Mymode = (ushort)moveP.Zero_Model;                      //回零方式为一次回零
            MyEZ_count = 0;                                         //保留参数

            double MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel;

            MyMin_Vel = moveP.Min_val;                              //起始速度 200pulse/s
            MyMax_Vel = moveP.Zero_val; ;                           //最大速度 5000pulse/s
            MyTacc = moveP.Tacc;                                    //加速时间 0.01s
            MyTdec = moveP.Tdec;                                    //减速时间 0.01s
            MyStop_Vel = moveP.Stop_val;                            //停止速度 200pulse/s

            //设置原点信号
            LTDMC.dmc_set_home_pin_logic(MyCardNo, Myaxis, Myorg_logic, Myfilter);
            //设置回原点模式
            LTDMC.dmc_set_homemode(MyCardNo, Myaxis, Myhome_dir, Myvel_mode, Mymode, MyEZ_count);
            //设置 轴脉冲输出方式
            //LTDMC.dmc_set_pulse_outmode(MyCardNo, Myaxis, 0);
            //设置 0 号轴梯形速度曲线参数
            LTDMC.dmc_set_profile_unit(MyCardNo, Myaxis, MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel);
            //执行回原点运动
            LTDMC.dmc_home_move(MyCardNo, Myaxis);

            WaitAxisComplete(Myaxis);

            uint status = LTDMC.dmc_axis_io_status(MyCardNo, Myaxis);
            string bin2_status = Convert.ToString(status, 2).PadLeft(12, '0');
            if (Convert.ToUInt16(bin2_status.Substring(7, 1)) == 1)
            {
                dmc_set_position(MyCardNo, Myaxis, 0);
            }
        }
        /// <summary>
        /// 回零成功否
        /// </summary>
        /// <param name="axis"></param>
        /// <returns></returns>
        public bool GetHomeStatus(ushort axis)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 坐标（绝对）移动_梯形
        /// </summary>
        /// <param name="axis">轴</param>
        /// <param name="location">坐标</param>
        public void Move_Location(ushort axis, double location)
        {
            if (BCAlertHelper.GetErrorQty) return;

            var moveP = BcFactory.Instance.Config.AxisParam.Where(p => p.AxisId == axis).FirstOrDefault();
            if (moveP == null)
            {
                throw new Exception("轴参数无设置");
            }
            ushort MyCardNo, Myaxis, Myposi_mode;
            double MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel;
            double MyDist;
            MyCardNo = _bcId;                               //卡号
            Myaxis = axis;                                                  //轴号
            MyMin_Vel = (double)moveP.Min_val;                              //起始速度 200pulse/s
            MyMax_Vel = (double)moveP.Max_val;                              //最大速度 5000pulse/s
            MyTacc = (double)moveP.Tacc;                                    //加速时间 0.01s
            MyTdec = (double)moveP.Tdec;                                    //减速时间 0.01s
            MyStop_Vel = (double)moveP.Stop_val;                            //停止速度 200pulse/s 
            MyDist = (double)location;                                              //位移为 6000pulse
            Myposi_mode = 1;                                                //0:相对，1:绝对坐标

            if (BcFactory.Instance.RunModel == StateEnum.手动模式)
                MyMax_Vel = (double)moveP.Manual_val;

            LTDMC.dmc_set_profile_unit(MyCardNo, Myaxis, MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel);
            LTDMC.dmc_set_s_profile(MyCardNo, Myaxis, 0, (double)moveP.S_Para);//设置单轴运动速度曲线 
            LTDMC.dmc_pmove_unit(MyCardNo, Myaxis, MyDist, Myposi_mode);       //执行点位运动
        }
        /// <summary>
        /// 定量(相对)移动
        /// </summary>
        /// <param name="axis">轴</param>
        /// <param name="direction">方向：0/反方向，1/正方向</param>
        /// <param name="ration_v">定量值</param>
        public void Move_Ration(ushort axis, ushort direction, double ration_v)
        {
            if (BCAlertHelper.GetErrorQty) return;
            var moveP = BcFactory.Instance.Config.AxisParam.Where(p => p.AxisId == axis).FirstOrDefault();
            if (moveP == null)
            {
                throw new Exception("轴参数无设置");
            }
            ushort MyCardNo, Myaxis, Myposi_mode;
            double MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel;
            double MyDist;
            MyCardNo = _bcId;                               //卡号
            Myaxis = axis;                                                  //轴号
            MyMin_Vel = (double)moveP.Min_val;                              //起始速度 200pulse/s
            MyMax_Vel = (double)moveP.Max_val;                              //最大速度 5000pulse/s
            MyTacc = (double)moveP.Tacc;                                    //加速时间 0.01s
            MyTdec = (double)moveP.Tdec;                                    //减速时间 0.01s
            MyStop_Vel = (double)moveP.Stop_val;                            //停止速度 200pulse/s 
            MyDist = direction == 0 ? -ration_v : ration_v;                 //位移为 6000pulse
            Myposi_mode = 0;                                                //0:相对，1:绝对坐标

            if (BcFactory.Instance.RunModel == StateEnum.手动模式)
                MyMax_Vel = (double)moveP.Manual_val;

            LTDMC.dmc_set_profile_unit(MyCardNo, Myaxis, MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel);
            LTDMC.dmc_set_s_profile(MyCardNo, Myaxis, 0, (double)moveP.S_Para);//设置单轴运动速度曲线 
            LTDMC.dmc_pmove_unit(MyCardNo, Myaxis, MyDist, Myposi_mode);    //执行点位运动
        }

        #endregion

        #region 轴参数
        /// <summary>
        /// 设置轴参数
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="model"></param>
        public void SetAxisParam(ushort axis, TB_AxisParam model)
        {
            var moveP = BcFactory.Instance.Config.AxisParam.Where(p => p.AxisId == axis).FirstOrDefault();
            if (moveP == null)
            {
                throw new Exception("轴参数无设置");
            }
            ushort MyCardNo, Myaxis, Myorg_logic, Myfilter, Myhome_dir, Myvel_mode, Mymode, MyEZ_count, outmode;
            double MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel;

            MyCardNo = _bcId;                                               //卡号
            Myaxis = axis;                                                  //轴号
            MyMin_Vel = (double)moveP.Min_val;                              //起始速度 200pulse/s
            MyMax_Vel = (double)moveP.Manual_val;                           //最大速度 5000pulse/s(起始速度）
            MyTacc = (double)moveP.Tacc;                                    //加速时间 0.01s
            MyTdec = (double)moveP.Tdec;                                    //减速时间 0.01s
            MyStop_Vel = (double)moveP.Stop_val;                            //停止速度 200pulse/s 

            Myorg_logic = 0;                                                //原点信号有效电平为低电平有效
            Myfilter = 0;                                                   //保留参数
            Myhome_dir = (ushort)moveP.Zero_dir;                            //回零方向
            Myvel_mode = 1;                                                 //回零模式为低速
            Mymode = (ushort)moveP.Zero_Model;                              //回零方式为一次回零
            MyEZ_count = 0;                                                 //保留参数
            outmode = (ushort)moveP.Plus_Model;                             //脉冲模式

            //设置当量
            SetEquiv(MyCardNo, Myaxis, moveP.Current_val);

            //设置运动速度
            LTDMC.dmc_set_profile_unit(MyCardNo, Myaxis, MyMin_Vel, MyMax_Vel, MyTacc, MyTdec, MyStop_Vel);
            //设置单轴运动速度曲线
            LTDMC.dmc_set_s_profile(MyCardNo, Myaxis, 0, (double)moveP.S_Para);

            //设置回零
            //设置原点开关的有效电平
            LTDMC.dmc_set_home_pin_logic(MyCardNo, Myaxis, Myorg_logic, Myfilter);
            //设置回原点模式
            LTDMC.dmc_set_homemode(MyCardNo, Myaxis, Myhome_dir, Myvel_mode, Mymode, MyEZ_count);

            //设置 轴脉冲输出方式
            LTDMC.dmc_set_pulse_outmode(MyCardNo, Myaxis, outmode);
        }
        public string GetAxisStatus(ushort cardNo, ushort axis)
        {
            uint bit = dmc_axis_io_status(cardNo, axis);
            return Convert.ToString(bit, 2);
        }

        public double GetAxisPosition(ushort axis)
        {
            double pos = 0;
            dmc_get_position_unit(_bcId, axis, ref pos);
            return pos;
        }
        public void SetAxisPosition(ushort axis, int position)
        {
            dmc_set_position(_bcId, axis, position);
        }

        public void SetSevon(ushort axisId, bool isEnabled)
        {
            ushort v = isEnabled ? (ushort)1 : (ushort)0;
            dmc_write_sevon_pin(_bcId, axisId, v);
        }
        public void SetSevonAll(bool enabled)
        {
            foreach (var item in BcFactory.Instance.Config.AxisParam)
            {
                SetSevon((ushort)item.AxisId, enabled);
            }
        }
        /// <summary>
        /// 导入config文档
        /// </summary>
        /// <param name="fileName"></param>
        public void LoadConfig(string fileName)
        {
            short errCode = dmc_download_configfile(_bcId, fileName);
            if (errCode != 0)
            {
                throw new Exception("配置文档导入失败:" + errCode);
            }
        }

        /// <summary>
        /// 设置正负限位
        /// </summary>
        /// <param name="cardNo">板卡</param>
        /// <param name="axis">轴</param>
        /// <param name="n_limit">负限位</param>
        /// <param name="p_limit">正限位</param>
        public void SetSoftLimit(ushort cardNo, ushort axis, int n_limit, int p_limit)
        {
            ushort enabled = 1;         //使能状态，0：禁止，1：允许
            ushort source_sel = 0;      //计数器选择，0：指令位置计数器，1：编码器计数器
            ushort sl_action = 0;       //限位停止方式，0：立即停止 1：减速停止
            dmc_set_softlimit(cardNo, axis, enabled, source_sel, sl_action, n_limit, p_limit);
        }
        /// <summary>
        /// 设置脉冲当量
        /// </summary>
        /// <param name="cardNo">板卡</param>
        /// <param name="axis">轴</param>
        /// <param name="equiv">脉冲量</param>
        public void SetEquiv(dynamic cardNo, ushort axis, double equiv)
        {
            LTDMC.dmc_set_equiv(cardNo, axis, equiv);
        }
        /// <summary>
        /// 获取脉冲当量
        /// </summary>
        /// <param name="cardNo"></param>
        /// <param name="axis"></param>
        /// <returns></returns>
        public double GetEquiv(ushort cardNo, ushort axis)
        {
            double v = 0;
            LTDMC.dmc_get_equiv(cardNo, axis, ref v);
            return v;
        }
        public void SetOut(ushort outPos, ushort low_high)
        {
            LTDMC.dmc_write_outbit(_bcId, outPos, low_high);
        }
        public int GetOut(int outPos)
        {
            return LTDMC.dmc_read_outbit(_bcId, (ushort)outPos);
        }
        public void DownAxisParam()
        {
            //下载参数
            foreach (var item in BcFactory.Instance.Config.AxisParam)
            {
                SetAxisParam(item.AxisId, item);
                //屏蔽此项，各个设备设置还不一致，可能跟接线有关系吧
                //参照说明208页
                dmc_set_alm_mode(_bcId, item.AxisId, 1, 1, 0);

                //打开使能
                dmc_write_sevon_pin(_bcId, item.AxisId, 0);

            }
        }
        public bool IsMoving()
        {
            bool moving = false;
            foreach (var item in BcFactory.Instance.Config.AxisParam)
            {
                int i = item.AxisId;
                moving = LTDMC.dmc_check_done(0, (ushort)i) == 0 ? true : false;
                if (moving)
                    return moving;
            }

            return moving;
        }

        public void ClearAlart(ushort axisId)
        {

        }

        public int GetIn(int outPos)
        {
            return dmc_read_inbit(_bcId, (ushort)outPos);
        }
        public void SetAxisPosition(ushort axis, double position)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region 私有方法
        private void Monitor(object obj)
        {
            MonitorModel m_Data = new MonitorModel();
            try
            {
                #region IO
                //参照DMC5000系列说明中210页
                var _in = dmc_read_inport(_bcId, 0);
                var _out = dmc_read_outport(_bcId, 0);
                var bin_in1 = Convert.ToString(_in, 2).PadLeft(16, '0').Reversal().ToList();
                for (int i = 0; i < 16; i++)
                {
                    m_Data.DI.Add(Convert.ToInt32(bin_in1[i].ToString()));
                }

                var bin_out = Convert.ToString(_out, 2).PadLeft(16, '0').Reversal().ToList();
                for (int i = 0; i < 16; i++)
                {
                    m_Data.DO.Add(Convert.ToInt32(bin_out[i].ToString()));
                }
                #endregion

                foreach (var item in BcFactory.Instance.Config.AxisParam)
                {
                    ushort id = item.AxisId;

                    #region 轴坐标
                    double pos = GetAxisPosition(id);
                    #endregion

                    #region 轴状态

                    //也可以使用dmc_axis_io_status获取状态
                    //m_Data.AxisState.Add(id, new AxisStatus()
                    //{
                    //    使能 = Convert.ToInt32(bin_out[16 + id].ToString()), //dmc_read_sevon_pin(_bcId, id),
                    //    原点 = Convert.ToInt32(bin_in2[0 + id].ToString()),
                    //    正限位 = Convert.ToInt32(bin_in1[16 + id].ToString()),
                    //    负限位 = Convert.ToInt32(bin_in1[24 + id].ToString()),
                    //});
                    var axisState = dmc_axis_io_status(_bcId, id);
                    var bin_axis = Convert.ToString(axisState, 2).PadLeft(12, '0').Reversal().ToList();
                    if (Convert.ToInt32(bin_axis[0].ToString()) == 1)
                    {
                        //通知全局
                        BcFactory.Instance.RunState(RunEnum.急停);
                        BCAlertHelper.Log($"{item.Name}报警", LogLevel.严重);
                    }

                    m_Data.AxisState.Add(id, new AxisStatus()
                    {
                        报警 = Convert.ToInt32(bin_axis[0].ToString()),
                        正限位 = Convert.ToInt32(bin_axis[1].ToString()),
                        负限位 = Convert.ToInt32(bin_axis[2].ToString()),
                        原点 = Convert.ToInt32(bin_axis[4].ToString()),
                        使能 = dmc_read_sevon_pin(_bcId, id),
                        位置 = GetAxisPosition(id)
                    });
                    #endregion
                }
                PubSub.Hub.Default.PublishAsync(m_Data);
            }
            catch (Exception ex)
            {
                Log4.Error($"【板卡监控】{ex.Message}", ex);
            }
        }
        /// <summary>
        /// <see cref="GetAlm(int)"/>
        /// </summary>
        /// <param name="axisId"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public AlmModel GetAlm(ushort axisId)
        {
            AlmModel model = new AlmModel();
            ushort stop = 0;
            ushort enable = 0;
            ushort val = 0;
            dmc_get_alm_mode(_bcId, axisId, ref enable, ref val, ref stop);
            model.ALM使能 = enable;
            model.ALM电平 = val;
            return model;
        }
        /// <summary>
        /// <see cref="SetAlm(ushort, ushort, ushort)"/>
        /// </summary>
        /// <param name="axisId"></param>
        /// <param name="enable"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public int SetAlm(ushort axisId, ushort enable, ushort val)
        {
            return dmc_set_alm_mode(_bcId, axisId, enable, val, 0);
        }

        public List<int> GetIn()
        {
            return null;
        }

        public List<int> GetOut()
        {
            return null;
        }
        #endregion
    }
}
